/**
 * 该模块定义方法以支持对象的相等比较。
 * License: BSD
 * Authors: Lucifer (786325481@QQ.com)
 * Copyright: Copyright (C) 2008 Lucifer. All rights reserved.
 */
module system.collections.EqualityComparer;

import system.collections.IEqualityComparer;
import system.Traits;
import system.Hash;
import system.math.IEEE;

/**
 * 为 IEqualityComparer(T) 泛型接口的实现提供默认比较器。
 * Comments: 如果需要自定义实现，可提供 IEqualityComparer(T) 泛型接口的自定义实现。
 */
public class DefaultEqualityComparer(T) : IEqualityComparer!(T)
{
	static if( IsPointerType!(T) || (IsArray!(T) && !IsSomeString!(T)) )
	{
		static assert( "Type " ~ T.stringof ~ " is not supported." );
	}
    else static if( is( T == struct ) && !( is( typeof(T.opEquals) ) ) )
    {
        static assert(false, "Struct " ~ T.stringof ~ " should overload opEquals.");
    }

    private static DefaultEqualityComparer!(T) theInstance;

    static this()
    {
        theInstance = new DefaultEqualityComparer!(T)();
    }

	static ~this()
	{
		if( theInstance !is null )
		{
			delete theInstance;
			scope(failure) return;
		}
	}

    /**
     * 获取默认比较器实例。
     */
    public static DefaultEqualityComparer!(T) getInstance()
    {
        return theInstance;
    }

    /**
     * 确定两个类型为 T 的对象是否相等。
     * Params: x = 要比较的第一个对象。
     *         y = 要比较的第二个对象。
     * Returns: 如果指定的对象相等，则为 true；否则为 false。
     * Comments: equals 方法是自反的、对称的和可传递的。
     *           也就是说，如果此方法用于将某个对象与其自身比较，则它将返回 true；
     *           如果对 y 和 x 执行此方法返回 true，则对 x 和 y 这两个对象也返回 true；
     *           如果对 x 和 y 执行此方法返回 true，并且对 y 和 z 执行此方法也返回 true，则对 x 和 z 这两个对象也返回 true。
     *
     *
     * 每一个想要进行相等或大小比较的对象都应该重载
     * opEquals, opCmp, toHash
     * Object.opEquals, Object.opCmp, Object.toHash
     * 的实现是有问题的。
     */
    public final bool equals(T x, T y)
    {
        //类类型
        static if( is( T == class ) )
        {
			
            return (x is y) || ( (x !is null) && (x.opEquals(y)) );

        }
        //接口类型
        else static if( is( T == interface ) )
        {
            Interface* pi = **cast(Interface***)cast(void*)x;
            Object o1 = cast(Object)( cast(void*)x - pi.offset);
            pi = **cast(Interface***)cast(void*)y;
            Object o2 = cast(Object)( cast(void*)y - pi.offset);

            return o1 == o2 || (o1 && o1.opCmp(o2) == 0);
        }
        //整型和字符类型
        else static if( IsIntegerType!(T) || IsCharType!(T) )
        {
            return (x == y);
        }
        //实数和虚数类型
        else static if( IsRealType!(T) || IsImaginaryType!(T) )
        {
            return (x == y) || ( isNaN(x) && isNaN(y) );
        }
        //复数类型
        else static if( IsComplexType!(T) )
        {
            return (x == y);
        }
        //结构类型
        else static if( is ( T == struct) && is(typeof(T.opEquals) == function)  )
        {
            return (x.opEquals(y));
        }
        else
        {
            return cast(bool)(typeid(T).equals(&x, &y));
        }
    }

    /**
     * 用作指定对象的哈希算法和数据结构（如哈希表）的哈希函数。
     */
    public final hash_t toHash(T obj)
    {
        return system.Hash.toHash(obj);
    }
}

unittest
{
    interface A{}

    class B : A
    {
        this(int i)
        {
            this.I = i;
        }
        int I;

        override bool opEquals(Object other)
        {
            if(other is null) return false;
            B b = cast(B)other;
            if(b is null) return false;
            return this.I == b.I;
        }

        override int opCmp(Object other)
        {
            if(other is null) return -1;
            auto b = cast(B)other;
            if( b is null ) return -1;
            return this.I - b.I;
        }

        hash_t toHash()
        {
            return I;
        }

    }

    A a1 = new B(1);
    A a2 = new B(1);

    DefaultEqualityComparer!(A) cmpA = DefaultEqualityComparer!(A).getInstance();
    assert( cmpA.equals(a1, a2) );
    assert( cmpA.toHash(a1) == cmpA.toHash(a2) );

    //assert( !cmpA.equals(a1, null) );

    B b1 = cast(B)a1;
    B b2 = cast(B)a2;
    DefaultEqualityComparer!(B) cmpB = DefaultEqualityComparer!(B).getInstance();
    assert( cmpB.equals(b1, b2) );
    assert( !cmpB.equals(b1, null) );
    assert( cmpB.toHash(b1) == cmpA.toHash(b2) );

    struct C
    {
        int I;
        bool opEquals(C other)
        {
            return this.I == other.I;
        }
        hash_t toHash()
        {
            return I;
        }
    }

    C c1, c2;
    c1.I = 1;
    c2.I = 1;
    DefaultEqualityComparer!(C) cmpC = DefaultEqualityComparer!(C).getInstance();
    assert( cmpC.equals(c1, c2) );
    assert( cmpC.toHash(c1) == cmpC.toHash(c2) );

    DefaultEqualityComparer!(int) cmpInt = new DefaultEqualityComparer!(int);
    assert( cmpInt.equals(1, 1) );
    assert( !(cmpInt.toHash(1) == cmpInt.toHash(2)) );

    DefaultEqualityComparer!(double) cmpDouble = new DefaultEqualityComparer!(double);
    assert( cmpDouble.equals(2.0L, 2.0L) );
    assert( !cmpDouble.equals(2.0L, double.nan) );
}
